package com.itchen.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itchen.annotation.Log;
import com.itchen.domain.entity.OperationLog;
import com.itchen.domain.entity.User;
import com.itchen.service.OperationLogService;
import com.itchen.utils.JwtTokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 *@description 请求响应日志 AOP
 *@author <a href="https://gitee.com/Double-CE">chen</a>
 */
@Aspect
@Component
@Slf4j
public class LogInterceptor {

    @Resource
    private OperationLogService operationLogService;

    @Resource
    private ObjectMapper objectMapper;

    /**
     * 请求前置拦截，目的：在控制台日志中记录请求信息，通过控制台排查
     */
    @Around("execution(* com.itchen.controller.*.*.*(..))")
    public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
        // 计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 获取请求路径
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
        // 生成请求唯一 id
        String requestId = UUID.randomUUID().toString();
        String url = httpServletRequest.getRequestURI();
        // 获取请求参数
        Object[] args = point.getArgs();
        String reqParam = "[" + StringUtils.join(args, ", ") + "]";
        // 输出请求日志
        log.info("request start, path: {}, ip: {}, params: {}", url,
                httpServletRequest.getRemoteHost(), reqParam);
        // 执行原方法
        Object result = point.proceed();
        // 输出响应日志
        stopWatch.stop();
        long totalTimeMillis = stopWatch.getTotalTimeMillis();
        log.info("request end, cost: {}ms", totalTimeMillis);
        return result;
    }

    /**
     * 设置操作日志切入点，记录操作日志
     * 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.itchen.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        try {
            // 获取当前用户
            User user = JwtTokenUtils.getCurrentUser();
            if (user == null) {
                return;
            }

            // 获取HttpServletRequest
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }
            HttpServletRequest request = attributes.getRequest();

            // 跳过对操作日志自身接口的记录，避免重复记录
            String requestURI = request.getRequestURI();
            if (requestURI.startsWith("/operationLog/")) {
                return;
            }

            // 获取注解信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Log logAnnotation = method.getAnnotation(Log.class);

            // 获取请求参数
            Map<String, Object> paramMap = new HashMap<>();
            // 获取请求的参数名称和参数值
            String[] paramNames = signature.getParameterNames();
            Object[] paramValues = joinPoint.getArgs();
            if (paramNames != null && paramNames.length > 0) {
                for (int i = 0; i < paramNames.length; i++) {
                    if (paramValues[i] != null) {
                        paramMap.put(paramNames[i], paramValues[i]);
                    }
                }
            }

            // 创建操作日志对象
            OperationLog operationLog = OperationLog.builder()
                    .userId(String.valueOf(user.getId()))
                    .username(user.getUsername())
                    .operationType(logAnnotation.operationType())
                    .operationModule(logAnnotation.operationModule())
                    .ipAddress(getClientIp(request))
                    .requestMethod(request.getMethod())
                    .requestUri(request.getRequestURI())
                    .requestParams(objectMapper.writeValueAsString(paramMap))
                    .build();

            // 保存操作日志
            operationLogService.save(operationLog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

